/*
 *
 * FileName: SCMNodeImpl.java
 * Author:   wx:fdzhangc
 * Date:     2021/11/24 11:52
 * Description: //模块目的、功能描述
 * History: //修改记录
 * <author>      <time>      <version>    <desc>
 * 修改人姓名    修改时间    版本号       描述
 */
package com.zhangc.zcscm.client;

import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zhangc.zcscm.util.MD5Utils;
import com.zhangc.zcscm.util.ParamUtil;
import com.zhangc.zcscm.util.ServerNameGetter;
import com.zhangc.zcscm.zookeeper.DataListener;
import com.zhangc.zcscm.zookeeper.ZkNode;
import com.zhangc.zcscm.zookeeper.exception.DataException;

/**
 * 功能描述:<br>
 *
 * @author wx:fdzhangc
 * @see [相关类/方法]（可选）
 * @since [产品/模块版本] （可选）
 */
public class SCMNodeImpl implements SCMNode, DataListener<String> {
    private static Logger logger = LoggerFactory.getLogger(SCMNodeImpl.class);
    private static Logger statLogger = LoggerFactory.getLogger("scm.statistic");
    private static final String SYNC_FAIL = "_SCM_SYNC_FAIL_";
    private static final String PATH_SEPARATOR;
    private String path;
    private ZkNode<String> zkNode;
    private volatile String value;
    private SCMConfiguration configuration;
    private static final String FILE_SEPARATOR = "-";

    public SCMNodeImpl(SCMConfiguration configuration, String path, ZkNode<String> zkNode) {
        this.path = path;
        this.zkNode = zkNode;
        this.configuration = configuration;
    }

    public void dataChanged(String oldData, String newData) {
        this.value = newData;
        this.saveToLocalFileCache(this.path, this.value);
    }

    public String getValue() throws SCMException {
        if ("_SCM_SYNC_FAIL_".equals(this.value)) {
            throw new SCMException("Sync fail.");
        } else {
            return this.value;
        }
    }

    public void sync() throws SCMException {
        try {
            this.zkNode.sync(false);
            this.value = (String) this.zkNode.getData();
            this.logToStat("init");
            this.saveToLocalFileCache(this.path, this.value);
        } catch (DataException var8) {
            logger.warn("Can not get config content of " + this.path + ",will try to load from local file.", var8);

            try {
                this.value = this.loadFromLocalFileCache(this.path);
            } catch (Exception var7) {
                logger.error("Exception when Load " + this.path + " from local file,Sync fail.", var7);
                this.value = "_SCM_SYNC_FAIL_";
                throw new SCMException("Sync " + this.path + " fail.", var7);
            }
        } finally {
            this.zkNode.monitor(this.value, this);
        }

    }

    /**
     * @deprecated
     */
    @Deprecated
    public void monitor(SCMListener scmListener) {
        this.monitor(this.value, scmListener);
    }

    public void monitor(String expect, final SCMListener scmListener) {
        this.zkNode.monitor(expect, new DataListener<String>() {
            public void dataChanged(String oldData, String newData) {
                SCMNodeImpl.this.logToStat("monitor");
                scmListener.execute(oldData, newData);
            }
        });
    }

    public void destroy() {
        this.zkNode.destroy();
    }

    private String loadFromLocalFileCache(String path) throws Exception {
        File file = new File(this.getLocalFilePath(this.configuration.getAppCode(), this.configuration.getScmServer(), path));
        if (!file.exists()) {
            throw new IOException("LocalFile " + file.getPath() + " not exist.");
        } else {
            FileInputStream in = null;
            //int size = true;
            StringBuilder sb = new StringBuilder(512);

            try {
                in = new FileInputStream(file);
                //  int length = true;
                byte[] data = new byte[8192];

                int n;
                while ((n = in.read(data)) != -1) {
                    sb.append(new String(data, 0, n, "UTF-8"));
                }

                String var9 = sb.toString();
                return var9;
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException var16) {
                        logger.error(var16.getMessage());
                    }
                }

            }
        }
    }

    private synchronized void saveToLocalFileCache(String path, String value) {
        FileOutputStream out = null;
        PrintWriter writer = null;

        try {
            File file = new File(this.getLocalFilePath(this.configuration.getAppCode(), this.configuration.getScmServer(), path));
            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (RuntimeException var16) {
                    logger.error(var16.getMessage(), var16);
                }
            }

            out = new FileOutputStream(file);
            BufferedOutputStream stream = new BufferedOutputStream(out);
            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(stream, "UTF-8")));
            writer.write(value == null ? "" : value);
            writer.flush();
        } catch (IOException var17) {
            logger.error("save localFile config error, path {} ",
                    this.configuration.getAppCode() + "-" + this.configuration.getScmServer() + "-" + path, var17);
        } finally {
            if (writer != null) {
                writer.close();
            }

            if (out != null) {
                try {
                    out.close();
                } catch (IOException var15) {
                    logger.error(var15.getMessage());
                }
            }

        }

    }

    private String getLocalFilePath(String appCode, String scmServer, String path) {
        if (path.contains(PATH_SEPARATOR)) {
            path = StringUtils.substringAfterLast(path, PATH_SEPARATOR);
        }

        String filePath = ParamUtil.getCachePath();
        if (StringUtils.isEmpty(filePath)) {
            filePath = SCMConstant.FILE_PATH;
        }

        String res = filePath + PATH_SEPARATOR + StringFilter(
                MD5Utils.getMD5(appCode + "-" + scmServer + "-" + ServerNameGetter.getServerName() + "-" + path));
        logger.info("Local File Path is {} ", res);
        return res;
    }

    public static String StringFilter(String str) throws PatternSyntaxException {
        Pattern p = Pattern.compile("[`~!@#$%^&*()+=|{}':;',//[//].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。，、？]");
        Matcher m = p.matcher(str);
        return m.replaceAll("").trim();
    }

    private void logToStat(String type) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("type", type);
        jsonObject.put("currentTime", System.currentTimeMillis());
        jsonObject.put("appcode", this.configuration.getAppCode());
        jsonObject.put("path", this.path);
        //jsonObject.put("ip", NetUtils.getLocalHost());
        jsonObject.put("ldc", System.getenv("ldc"));
        //jsonObject.put("server", ProcessUtils.getProcessId());
        statLogger.info(JSON.toJSONString(jsonObject));
    }

    static {
        PATH_SEPARATOR = File.separator;
    }
}
